package cn.com.sinux.spring.utils.image;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ImagingOpException;
import java.io.File;
import java.io.IOException;

/**
 * @author <a href="mailto:oznyang@163.com">oznyang</a>
 */
public class AwtImageTransform implements ImageTransform {
    private static final Logger logger = LoggerFactory.getLogger(AwtImageTransform.class);
    private BufferedImage srcBImage;
    private BufferedImage destBImage;
    private int imgWidth;
    private int imgHeight;
    String[] imagePostfix = new String[]{"jpg", "png"};

    private BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }

        // This code ensures that all the pixels in the image are loaded
        image = new ImageIcon(image).getImage();

        // Determine if the image has transparent pixels; for this method's
        // implementation, see e661 Determining If an Image Has Transparent Pixels
        //boolean hasAlpha = hasAlpha(image);

        // Create a buffered image with a format that's compatible with the screen
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            // Determine the type of transparency of the new buffered image
            int transparency = Transparency.OPAQUE;
       /* if (hasAlpha) {
		 transparency = Transparency.BITMASK;
	     }*/

            // Create the buffered image
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(
                    image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
            // The system does not have a screen
        }

        if (bimage == null) {
            // Create a buffered image using the default color model
            int type = BufferedImage.TYPE_INT_RGB;
            //int type = BufferedImage.TYPE_3BYTE_BGR;//by wang
	    /*if (hasAlpha) {
		 type = BufferedImage.TYPE_INT_ARGB;
	     }*/
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }

        // Copy image to buffered image
        Graphics g = bimage.createGraphics();

        // Paint the image onto the buffered image
        g.drawImage(image, 0, 0, null);
        g.dispose();

        return bimage;
    }

    public boolean load(BufferedImage bufferedImage, String srcFilePath) {
        if (srcFilePath == null) {
            logger.warn("Source file path is null!");
            return false;
        }
        File file = new File(srcFilePath);
        if (!(file.exists() && file.isFile())) {
            logger.warn("file '{}' is not exists or cannot read.", srcFilePath);
            return false;
        }
        try {
            //Toolkit toolkit = Toolkit.getDefaultToolkit();
            //Image srcImage = toolkit.getImage(file.getPath()); // 构造Image对象
            //srcBImage = toBufferedImage(srcImage);
            if (bufferedImage == null) {
                bufferedImage = ImageIO.read(file);
            }
            srcBImage = bufferedImage;
            imgWidth = /*((ToolkitImage)srcImage)*/srcBImage.getWidth();
            imgHeight = /*((ToolkitImage)srcImage)*/srcBImage.getHeight();
        } catch (Exception e) {
            String msg = "Cannot open image [" + srcFilePath + "]";
            logger.warn(msg);
            return false;
        }

        return true;
    }

    public boolean load(String srcFilePath) {
        if (srcFilePath == null) {
            logger.warn("Source file path is null!");
            return false;
        }
        File file = new File(srcFilePath);
        if (!(file.exists() && file.isFile())) {
            logger.warn("file '{}' is not exists or cannot read.", srcFilePath);
            return false;
        }
        try {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            //Image srcImage = toolkit.getImage(file.getPath()); // 构造Image对象
            //srcBImage = toBufferedImage(srcImage);
            srcBImage = ImageIO.read(file);
            imgWidth = /*((ToolkitImage)srcImage)*/srcBImage.getWidth();
            imgHeight = /*((ToolkitImage)srcImage)*/srcBImage.getHeight();
        } catch (Exception e) {
            String msg = "Cannot open image [" + srcFilePath + "]";
            logger.warn(msg);
            return false;
        }

        return true;
    }

    public boolean save(String destFilePath) {
        if (destBImage == null) {
            return false;
        }

        String postfix = getPostfix(destFilePath);
        try {
            ImageIO.write(destBImage, postfix, new File(destFilePath));
        } catch (IOException e) {
            String msg = "Cannot open image [" + destFilePath + "]";

            logger.info(msg);

            return false;
        }

        return true;
    }

    private String getPostfix(String destFilePath) {
        String postfix = destFilePath.substring(destFilePath.lastIndexOf(".") + 1);
        boolean postfixPermitted = false;
        for (int i = 0; i < imagePostfix.length; i++) {
            if (postfix.equalsIgnoreCase(imagePostfix[i]))
                postfixPermitted = true;
        }
        if (!postfixPermitted)
            postfix = imagePostfix[0];
        return postfix;
    }

    public void resize(int width, int height) {
        double xRatio = (double) height / imgHeight;
        double yRatio = (double) width / imgWidth;
        double ratio = (xRatio < yRatio) ? xRatio : yRatio;
        zoomImage(imgWidth, imgHeight, ratio);
    }

    public void rotate(double rotate) {
        rotateImage(imgWidth, imgHeight, rotate, 0);
    }

    public void resizeWithMaxWidth(int maxWidth) {
        if (imgWidth > maxWidth) {
            double ratio = (double) maxWidth / imgWidth;
            zoomImage(imgWidth, imgHeight, ratio);
        } else {
            destBImage = srcBImage;
        }
    }

    public void rotateWithMaxWidth(double rotate, int maxWidth) {
        rotateImage(imgWidth, imgHeight, rotate, maxWidth);
    }

    /**
     * 以像素为单位截取图像.
     *
     * @param left   左坐标.
     * @param top    顶坐标.
     * @param width  宽度.
     * @param height 高度.
     */
    public void crop(final int left, final int top, final int width, final int height) {
        destBImage = srcBImage.getSubimage(left, top, width, height);
    }

    private void zoomImage(int width, int height, double ratio) {
        AffineTransform transform = new AffineTransform();
        transform.setToScale(ratio, ratio);    // set zoom size
        AffineTransformOp ato = new AffineTransformOp(transform, null);
        try {
            destBImage = new BufferedImage((int) (width * ratio), (int) (height * ratio), srcBImage.getType());
            ato.filter(srcBImage, destBImage);
        } catch (Exception e) {
            throw new ImagingOpException("Unable to transform src image");
        }

        /*AffineTransformOp ato =
                new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
        try {
            //destBImage = new BufferedImage((int) (width * ratio), (int) (height * ratio), srcBImage.getType());
            //destBImage = (BufferedImage) srcBImage.getScaledInstance((int) (width * ratio), (int) (height * ratio), srcBImage.SCALE_SMOOTH);
            destBImage = ato.filter(srcBImage, null);
        } catch (Exception e) {
            throw new ImagingOpException("Unable to transform src image");
        }*/
    }

    private void rotateImage(int width, int height, double rotate, int maxWidth) {
        double radian = rotate * Math.PI / 180;                             // get radian
        double h = height * Math.cos(radian) + width * Math.sin(radian);    // get rotated width
        double w = height * Math.sin(radian) + width * Math.cos(radian);    // get rotated height
        double ratio = maxWidth != 0 && w > maxWidth ? maxWidth / w : 1;    // if maxWidth=0 do not zoom
        AffineTransform transform = new AffineTransform();

        transform.setToScale(ratio, ratio);
        transform.rotate(radian, w / 2, h / 2);
        transform.translate(w / 2 - width / 2, h / 2 - height / 2);
        AffineTransformOp ato = new AffineTransformOp(transform, null);

        w *= ratio;
        h *= ratio;

        try {
            destBImage = new BufferedImage((int) w, (int) h, srcBImage.getType());
            Graphics gs = destBImage.getGraphics();

            gs.setColor(Color.white);    // set canvas background to white
            gs.fillRect(0, 0, (int) w, (int) h);
            ato.filter(srcBImage, destBImage);
        } catch (Exception e) {
            throw new ImagingOpException("Unable to transform src image");
        }
    }
}
